JS - Geordende tabellen
- We maken het mogelijk om tabellen de ordenen op de inhoud van één bepaalde kolom.
- Je kan per kolom aangeven als de ordening dalend of stijgend moet zijn.
- Die functionaliteit moet aan meer dan één tabel kunnen toegevoegd worden door middel van slechts één instructie.
- Die functionaliteit moet zowel via het toetsenbord als via de muis toegankelijk zijn.
- De code vind je terug op Bitbucket.
Video
Constructor functie
Om de code te kunnen toepassen op om het even welke tabel moeten we een constructor-functie schrijven (zie JS - Object Constructor). Van deze functie maken voor elke tabel, die we sorteerbaar willen maken, een instantie:
function TableSort(id, continentalNotation = true) { // When calling an object constructor or any of its methods, // this’ refers to the instance of the object // much like any class-based language this.continentalNotation = continentalNotation; this.tableElement = document.getElementById(id); if (this.tableElement && this.tableElement.nodeName == "TABLE") { this.prepare(); } }
De code hierboven maakt een constructor-functie met de naam TableSort
. Met erop dat de naam van deze functie in pascal-notatie geschreven en niet in camelcase zoals dat de gewoonte is voor gewone functies.
Wanneer je de functie oproept geef je de id-selector
naam mee van de tabel. De functie gebruikt de getElementId
DOM methode om een referentie naar de tabel op te halen. Die referentie wordt opgeslagen in this.tableElement
. Het sleutelwoord this
verwijst hier naar instantie van de constructor-functie die met het new
sleutelwoord gemaakt zal worden. De code test als de opgegeven id-selector overeenkomt met een table element en indien dat zo is wordt de prepare methode uitgevoerd. Deze methode doet het werk om de tabel sorteerbaar te maken. Het sorteren zelf gebeurt pas als de gebruiker op één van de koppen van de tabel klikt.
Met de tweede parameter geef je aan in welke notatie de getallen geschreven zijn. In de Engelse notatie worden kommagetallen gescheiden door een punt en duizenden door een komma. In de continentale schrijfwijze is het precies omgekeerd. Onze sort methode moet dus rekening kunnen houden met de manier waarop de getallen geschreven worden. We declareren daarvoor een eigenschap this.continentalNotation
en stellen die standaar in op true
.
De TableSort
roepen we pas op als de HTML volledig in de browser geladen is. Dat doen we bijvoorbeeld wanneer de browser het onload
event afvuurt:
window.onload = function() { var jommeke = new TableSort("jommeke"); var fruit = new TableSort("fruit"); }
Kolomkoppen toegankelijk maken
prepare
methode- De
prepare
methode voegen we aan onze functie toe met behulp van deprototype
eigenschap ervan (zie hiervoor JS - Objecten - Prototype). De prototype eigenschap is een object dat het toevoegen van een eigenschap of een methode aan alle instanties van een constructor-functie eenvoudig maakt:TableSort.prototype.prepare = function() { ... }
- In de
prepare
methode beginnen we met aan te geven dat de sorteervolgorde standaard stijgend is. Daarvoor voegen we aan elke kolomkop een pijltje naar boven toe. We gaan ervan uit dat als de gebruiker voor de eerste keer op een kolomkop klikt de kolom in stijgende volgorde gesorteerd zal worden.
Om te onthouden in welke volgorde de kolom gesorteerd zal worden wanneer de gebruiker op de kolomkop stellen we de klassennaam van hetth
element, die het sort event zal afvuren, in op asc. Om dit te realiseren:- maken we twee CSS klassen en gebruiken de
::after
selector om een pijltje achter de koptekst van de kolom toe te voegen. De klasseasc
voor een pijltje naar boven endesc
voor een pijltje naar beneden:/* css entities: https://www.w3schools.com/cssref/css_entities.asp */ .asc::after { content: "\0020\0020\0020\2191"; } .desc::after { content: "\0020\0020\0020\2193"; }
- doorlopen we de
headings
htmlcollectie met eenfor
lus en stellen de we css klasse van alleth
's in opasc
:TableSort.prototype.prepare = function () { // add arrow up // default is ascending order let headings = this.tableElement.tHead.rows[0].cells; // headings is een htmlcollection for (let i = 0; i < headings.length; i++) { headings[i].className = 'asc'; } }
- Tenslotte voegen we wat CSS toe om de intentie van UI duidelijker te maken. Als de gebruiker over de kolomkop zweeft met de muis wordt die in reverse getoond:
th:hover { cursor: hand; cursor: pointer; color: white; background-color: #630; }
- maken we twee CSS klassen en gebruiken de
- Nadat we het pijltje met behulp van de
asc
klasse aan de header hebben toegevoegd, moeten we nu een eventhandelaar toekennen die zal worden uitgevoerd als de gebruiker op een kolomkop van een tabel klikt. We koppelen de eventafhandelaar aan hetclick
event van de tabel.
Ons eerste idee is om de eventafhandelaar met de volgende code toe te voegenTableSort.prototype.prepare = function () { // add arrow up // default is ascending order let headings = this.tableElement.tHead.rows[0].cells; // headings is een htmlcollection for (let i = 0; i < headings.length; i++) { headings[i].className = 'asc'; } this.tableElement.addEventListener("click", this.eventHandler, false); }
Maar als je goed nadenkt zie je dat hier een probleem is. Je zou denken dat dethis
vóór de eventHandler verwijst naar de instantie vanTableSort
omdat je inTableSort
bezig bent te coderen. Maar vergeet niet dat een eventafhandelaar eigenlijk een callback functie is. De codethis.eventHandler
wordt niet afgevuurd op het moment dat de EventListener wordt toegevoegd maar op het moment dat het event wordt afgevuurd. Op dat moment verwijstthis
naar de kopkolom waarop geklikt werd en zalthis
dus verwijzen naar die kopkolom. En het kopkolom object beschikt natuurlijk niet over de methodesortColumn
. Dus op het moment dat we de EventListener toevoegen moeten we een verwijzing naar dethis
van hetSortTable
object onthouden. Dat doen we met behulp van een closure (zie Closures en Closures in de praktijk):TableSort.prototype.prepare = function () { // add arrow up // default is ascending order let headings = this.tableElement.tHead.rows[0].cells; // headings is een htmlcollection for (let i = 0; i < headings.length; i++) { headings[i].className = 'asc'; } this.tableElement.addEventListener("click", function (that) { return function (event) { that.eventHandler(event); } }(this), false); }
We hebben hier wel een mooi voorbeeld van het gevolg dat functies in JavaScript eersteklasrangburgers zijn:- anonieme functie: in de eventHandler geven we een anonieme functie mee die een anonieme functie retourneert
- closure: de buitenste anonieme functie maakt een closure die ervoor zorgt dat de referentie naar het object TableSort bij het afvuren van het klik event bekend is;
- een IIFE of Immediately-Invoked Function Expression: bij het toekennen van de anonieme functie aan de eventhandler wordt die anonieme functie onmiddelijk uitgevoerd, dat gebeurt dus tijdens het toekennen van de functie aan de eventhandler en niet op het moment dat gebruikt op de kolomkop klikt; dat doen we omdat we de referentie naar de this van TableSort met behulp van een closure willen meegeven aan de eventhandler die slechts zal worden uitgevoerd op het moment dat erop de kolomkop wordt geklikt;
- De
eventHandler
methode
De gebruiker moet kunnen aangeven dat de tabel geordend moet worden met behulp van het toetsenbord en van de muis. Toegankelijk voor de gebruiker wil zeggen dat we door de gebruiker afgevuurde events moeten kunnen ondervangen. We gebruiken hiervoor:addEventListener
van de DOM;-
de opgaande stroom van de events (zie hiervoor JS - Eventstroom): we gaan niet aan elke kolomkop afzonderlijk een eventafhandelaar toekennen.
We laten de afgevuurde events opborrelen tot aan het table element en pas aan het
table
element kennen we eenEventListener
toe die zal nagaan welk element in de tabel het event heeft afgevuurd. Op die manier houden we alle logica die te maken heeft met de interactie met de gebruiker in één overzichtelijke methode.Als het event werd afgevuurd door op een
th
element te klikken wordt de sorteer methode uitgevoerd. Om te verifiëren als erop een th is geklikt schrijven we het volgende statement:if (event.target.tagName === 'TH') { // alert('kolomkop'); this.sortColumn(event.target); ... }
Maar als er een nog een ander HTML element in het
th
element staat, in de kopteksten van de suiker tabel staat bijvoorbeeld<th><strong></strong>Product</strong></th>
, gaat de sort mehode niet uitgevoerd worden. Want er wordt niet op eenth
element geklikt maar op eenstrong
element. Daarom gaan we zoeken naar het eerst bovenliggendeth
element van het element waarop geklikt werd, in ons voorbeeld eenstrong
element. We gebruiken daarvoor deevent.target.closest('TH')
methode. Meer info hierover vind je op Detecting clicks inside an element with vanilla JavaScript.
TableSort.prototype.eventHandler = function (event) { // zoek het eerst bovenliggende TH element en sla het op in een variabele let elemTarget = event.target.closest('TH'); if (elemTarget.tagName === 'TH') { // alert('kolomkop'); // sorteer eerst in de richting van de pijl this.sortColumn(elemTarget); // draai de pijl om om de richting van de volgende // sort te bepalen als de gebruiker weer klikt op de kolomkop if (elemTarget.className === "asc") { elemTarget.className = 'desc'; } else { elemTarget.className = 'asc'; } } }
Het sorteer algoritme
Op het moment dat de gebruiker op een kolomkop klikt moet de tabel geordend worden op deze kolom. De eventHandler ondervangt dit klik-event en voert de sortColomn
methode uit.
- Onthouden in welke volgorde de kolom gesorteerd is. Daarvoor hallen we de klassennaam op van het
th
element waarop geklikt werd. - Initialiseer enkele variabelen:
rows
: deze variabele gebruiken als een shortcut die verwijst naar de tabelrijen, op die manier moeten we niet altijd de volledigethis.tableElement.rows
intypenalpha
,numeric
: in deze arrays slaan we alfanumerieke en numerieke inhoud van de cellen in de tabel op en de rijindex van de cel in een literal array van de vorm:{ value: 'de inhoud van cel' row: index van de rij waarin de cel staat }
aIndex
,nIndex
: deze variabelen gebruiken we als indices, telkens als we een element aan een van beide array's toevoegen wordt die met 1 vermeerderdcellIndex
: in deze variabele zetten wecellIndex
waarde van de kopkolom waarop geklikt werd.cellIndex
is een eigenschap van hetcol
DOM element en retourneert de plaats van de kolom in de rij waarin die zich bevindt.
TableSort.prototype.sortColumn = function(headerCell) { // onthouden in welke volgorde de kolom nu geordend is const order = headerCell.className; // Get cell data for column that is to be sorted from HTML table let rows = this.tableElement.rows; let alpha = [], numeric = []; let alphaIndex = 0, numericIndex = 0; let cellIndex = headerCell.cellIndex; . . . }
- Vervolgens doorlopen we elke rij van de tabel en bewerken de cel die onder de kolomkop valt waarop de gebruiker geklikt heeft. We willen alleen de tekst uit de cel halen en de eventuele HTML erin negeren. Daarvoor gebruiken we de
textContent
ofinnerText
eigenschap. Firefox ondersteunt alleentextContent
en IE alleeninnerText
. Andere browsers ondersteunen beiden. We gebruiken daarom de ternaire operator die de een of de andere eigenschap gebruikt afhankelijk van welke eigenschap in de browser voorhanden is.TableSort.prototype.sortColumn = function(headerCell) { ... for (let i = 1; rows[i]; i++) { let cell = rows[i].cells[cellIndex]; let content = cell.textContent ? cell.textContent : cell.innerText; alert(content) } }
- We moeten kunnen bepalen of de cel een numerieke waarde bevat of niet:
- Daarvoor moeten we eerst alle karakters verwijderen die ervoor kunnen zorgen dat de celinhoud geïnterpreteerd wordt als alfanumeriek zoals het euroteken of dollarteken enz. We verwijderen alle alfakaracters en wijzigen Europese notatie in Engelse.
Eventuele verbetering: aan de constructor meegeven of Engelse of contintentale notatie gebruikt wordt.
Dat doen we met een regulieren expressie (zie meer daarover op reguliere expressies). - Met de
parseFloat
functie gaan we na of de uitgezuiverde waarde een getal is of niet. - Indien het een getal is slaan we uitgezuiverde waarde en de referentie naar de rij waarin de cel staat, als een literal array, in de numeric array (in de laatste wijziging beschouwen we de tekst in de cel als getal als er cijfers in staan, de alpha tekst wordt verwijderd)
- anders in de alpha array.
- de literal array, de uitgezuiverde waarde en de referentie naar de rij waarin de betreffende kolom staat, hebben we later nodig om de geordende tabel weer op te bouwen;
TableSort.prototype.sortColumn = function(headerCell) { // onthoudt in welke volgorde de kolom nu geordend is const order = headerCell.className; // Get cell data for column that is to be sorted from HTML table let rows = this.tableElement.rows; let alpha = [], numeric = []; let alphaIndex = 0, numericIndex = 0; let cellIndex = headerCell.cellIndex; for (var i = 1; rows[i]; i++) { let cell = rows[i].cells[cellIndex]; let content = cell.textContent ? cell.textContent : cell.innerText; // let numericValue = content.replace(/(\€\$|\,|\s)/g, ""); // als er getallen instaan, verwijder alle karakters die geen getallen, // punt of komma zijn let numericValue; if (this.continentalNotation) { // vervang vervolgens komma door punt en punt door komma numericValue = content.replace(/[^0-9,.]*/g, "").replace(/[,.]/g, function (m) { // m is the match found in the string // If `,` is matched return `.`, if `.` matched return `,` return m === ',' ? '.' : ','; }); } else { numericValue = content.replace(/[^0-9,.]*/g, ""); } // alert(numericValue); if (parseFloat(numericValue) == numericValue) { numeric[numericIndex++] = { value: Number(numericValue), row: rows[i] } } else { alpha[alphaIndex++] = { value: content, row: rows[i] } } } }
- Daarvoor moeten we eerst alle karakters verwijderen die ervoor kunnen zorgen dat de celinhoud geïnterpreteerd wordt als alfanumeriek zoals het euroteken of dollarteken enz. We verwijderen alle alfakaracters en wijzigen Europese notatie in Engelse.
- De sort implementeren
We gaan niet zelf een sorteeralgoritme schrijven maar de
sort
methode van hetArray
object gebruiken (zie JS - array methoden - muterend). We gebruiken deorder
variabele om te kijken als we in dalende of stijgende volgorde moeten sorteren.
Voeg de volgende code toe aan het einde van deSortColumn
methode van hierboven:TableSort.prototype.sortColumn = function (headerCell) { // onthoudt in welke volgorde de kolom nu geordend is const order = headerCell.className; // alert(order); // Get cell data for column that is to be sorted from HTML table let rows = this.tableElement.rows; let alpha = [], numeric = []; let alphaIndex = 0, numericIndex = 0; let cellIndex = headerCell.cellIndex; for (let i = 1; rows[i]; i++) { let cell = rows[i].cells[cellIndex]; let content = cell.textContent ? cell.textContent : cell.innerText; // let numericValue = content.replace(/(\€\$|\,|\s)/g, ""); // als er getallen instaan, verwijder alle karakters die geen getallen, // punt of komma zijn let numericValue; if (this.continentalNotation) { // vervang vervolgens komma door punt en punt door komma numericValue = content.replace(/[^0-9,.]*/g, "").replace(/[,.]/g, function (m) { // m is the match found in the string // If `,` is matched return `.`, if `.` matched return `,` return m === ',' ? '.' : ','; }); } else { numericValue = content.replace(/[^0-9,.]*/g, ""); } // alert(numericValue); if (parseFloat(numericValue) == numericValue) { numeric[numericIndex++] = { value: Number(numericValue), row: rows[i] } } else { alpha[alphaIndex++] = { value: content, row: rows[i] } } } numeric.sort(function (a, b) { if (order === 'asc') { return a.value - b.value; } else { return b.value - a.value; } }); alpha.sort(function (a, b) { let aName = a.value.toLowerCase(); let bName = b.value.toLowerCase(); if (aName < bName) { if (order === 'asc') { return -1; } else { return 1 } } else if (aName > bName) { if (order === 'asc') { return 1; } else { return -1 } } else { return 0; } }); }
- De geordende tabel opnieuw genereren
Tensolotte moeten we de tabel opnieuw opbouwen maar nu gesorteerd op de inhoud van de geselecteerde kolom. Voeg daarvoor de volgende code toe aan het einde van de
SortColumn
methode van hierboven:TableSort.prototype.sortColumn = function (headerCell) { // onthoudt in welke volgorde de kolom nu geordend is const order = headerCell.className; // alert(order); // Get cell data for column that is to be sorted from HTML table let rows = this.tableElement.rows; let alpha = [], numeric = []; let alphaIndex = 0, numericIndex = 0; let cellIndex = headerCell.cellIndex; for (let i = 1; rows[i]; i++) { let cell = rows[i].cells[cellIndex]; let content = cell.textContent ? cell.textContent : cell.innerText; // let numericValue = content.replace(/(\€\$|\,|\s)/g, ""); // als er getallen instaan, verwijder alle karakters die geen getallen, // punt of komma zijn let numericValue; if (this.continentalNotation) { // vervang vervolgens komma door punt en punt door komma numericValue = content.replace(/[^0-9,.]*/g, "").replace(/[,.]/g, function (m) { // m is the match found in the string // If `,` is matched return `.`, if `.` matched return `,` return m === ',' ? '.' : ','; }); } else { numericValue = content.replace(/[^0-9,.]*/g, ""); } // alert(numericValue); if (parseFloat(numericValue) == numericValue) { numeric[numericIndex++] = { value: Number(numericValue), row: rows[i] } } else { alpha[alphaIndex++] = { value: content, row: rows[i] } } } numeric.sort(function (a, b) { if (order === 'asc') { return a.value - b.value; } else { return b.value - a.value; } }); alpha.sort(function (a, b) { let aName = a.value.toLowerCase(); let bName = b.value.toLowerCase(); if (aName < bName) { if (order === 'asc') { return -1; } else { return 1 } } else if (aName > bName) { if (order === 'asc') { return 1; } else { return -1 } } else { return 0; } }); let orderdedColumns = []; orderdedColumns = numeric.concat(alpha); let tBody = this.tableElement.tBodies[0]; for (let i = 0; orderdedColumns[i]; i++) { tBody.appendChild(orderdedColumns[i].row); } }
En hiermee is de
sortColumn
methode volledig afgewerkt.
Een JS library bestand maken
Tenslote halen we de JavaScript code voor het ordenen van een tabel uit de HTML en plaatsen het in een apart bestand met de naam tablesort.js in de map js.
De HTML
Als voorbeeld nemen we enkele Jommeke's albums en een tabel die aangeeft hoeveel calorieën er in 100 g fruit zit. We gebruiken twee tabellen omdat onze sorteerfunctie op meer dan één tabel op dezelfde pagina toegepast moet kunnen worden:
<table id="jommeke" class="spreadsheet"> <caption>Jommeke albums</caption> <col /> <col /> <col /> <col /> <col /> <col /> <thead> <tr> <th scope="col">Nummer</th> <th scope="col">Titel</th> <th scope="col">Kaft</th> <th scope="col">€</th> <th scope="col">¥</th> <th scope="col">£</th> </tr> </thead> <tbody> <tr> <th scope="row">1</th> <td>Jacht op een voetbal</td> <td>Softcover</td> <td>5,22</td> <td>34</td> <td>3,76</td> </tr> <tr> <th scope="row">2</th> <td>De zingende aap</td> <td>Softcover</td> <td>5,22</td> <td>34</td> <td>3,76</td> </tr> <tr> <th scope="row">3</th> <td>De Koningin van Onderland</td> <td>Hardcover</td> <td>8,22</td> <td>54,1</td> <td>5,91</td> </tr> <tr> <th scope="row">4</th> <td>Purperen pillen</td> <td>Softcover</td> <td>5,22</td> <td>34</td> <td>3,76</td> </tr> <tr> <th scope="row">5</th> <td>De Muzikale Bella</td> <td>Hardcover</td> <td>8,22</td> <td>54,1</td> <td>5,91</td> </tr> </tbody> </table>
De calorieëntabel voor fruit:
<table id="fruit" class="spreadsheet"> <thead> <tr> <th>Fruitsoort</th> <th>hoeveelheid</th> <th>calorieën</th> </tr> </thead> <tbody> <tr> <td>abrikoos met schil</td> <td>3 st. (100 gr)</td> <td align="right">48</td> </tr> <tr> <td>ananas</td> <td>100 gr</td> <td align="right">60</td> </tr> <tr> <td>appel met schil</td> <td>1 st. (100 gr)</td> <td align="right">52</td> </tr> <tr> <td>banaan</td> <td>1 st .</td> <td align="right">94</td> </tr> <tr> <td>blauwe bessen</td> <td>100 g</td> <td align="right">75</td> </tr> <tr> <td>citroen</td> <td>1 st.</td> <td align="right">17</td> </tr> <tr> <td>druiven</td> <td>100 g</td> <td align="right">54</td> </tr> <tr> <td>frambozen</td> <td>100 g</td> <td align="right">68</td> </tr> <tr> <td>grapefruit</td> <td>1 st.</td> <td align="right">82</td> </tr> <tr> <td>kersen</td> <td>100 g</td> <td align="right">64</td> </tr> <tr> <td>kiwi</td> <td>1 st</td> <td align="right">46</td> </tr> <tr> <td>nectarine</td> <td>1 st.</td> <td align="right">67</td> </tr> <tr> <td>peer</td> <td>1 st.</td> <td align="right">98</td> </tr> <tr> <td>perzik</td> <td>1 st.</td> <td align="right">42</td> </tr> <tr> <td>pruim</td> <td>1 st.</td> <td align="right">36</td> </tr> <tr> <td>sinaasappel</td> <td>1 st.</td> <td align="right">86</td> </tr> <tr> <td>watermeloen</td> <td>100 g</td> <td align="right">37</td> </tr> </tbody> </table>
De suiker tabel
<table id="fruit-sugar"> <caption>Suikerlijst fruit</caption> <thead> <tr> <th><strong>Product</strong></th> <th><strong>Per portie</strong></th> <th><strong>Suiker</strong></th> <th><strong>Energie</strong></th> </tr> </thead> <tbody> <tr> <td>Aardbeien</td> <td>1 schaaltje (= 100 g)</td> <td>5,1 g</td> <td>29 kcal</td> </tr> <tr> <td>Abrikozen, gedroogd</td> <td>1 stuks (= 9 g)</td> <td>5 g</td> <td>26 kcal</td> </tr> <tr> <td>Abrikozen, vers</td> <td>1 stuk (= 20 g)</td> <td>1,6 g</td> <td>9 kcal</td> </tr> <tr> <td>Ananas</td> <td>1 schaaltje (= 100 g)</td> <td>11,6 g</td> <td>57 kcal</td> </tr> <tr> <td>Appel</td> <td>1 stuk (= 135 g)</td> <td>14 g</td> <td>81 kcal</td> </tr> <tr> <td>Banaan</td> <td>1 middel (= 130 g)</td> <td>20,1 g</td> <td>124 kcal</td> </tr> <tr> <td>Blauwe bessen</td> <td>1 schaaltje (= 100 g)</td> <td>10 g</td> <td>52 kcal</td> </tr> <tr> <td>Blauwe druiven</td> <td>1 schaaltje (= 100 g)</td> <td>16,8 g</td> <td>75 kcal</td> </tr> <tr> <td>Cranberries, gedroogd</td> <td>1 schaaltje (= 100 g)</td> <td>64,6 g</td> <td>335 kcal</td> </tr> <tr> <td>Cranberries, vers</td> <td>1 schaaltje (= 100 g)</td> <td>3,4 g</td> <td>24 kcal</td> </tr> <tr> <td>Dadels, gedroogd, geconfijt</td> <td>1 stuk (= 6 g)</td> <td>4,2 g</td> <td>19 kcal</td> </tr> <tr> <td>Dadels, vers</td> <td>1 stuk (= 6 g)</td> <td>1,9 g</td> <td>8 kcal</td> </tr> <tr> <td>Frambozen</td> <td>1 schaaltje (= 100 g)</td> <td>4,5 g</td> <td>35 kcal</td> </tr> <tr> <td>Grapefruit</td> <td>1 stuk (= 150 g)</td> <td>10 g</td> <td>28 kcal</td> </tr> <tr> <td>Kaki</td> <td>1 stuk (= 150 g)</td> <td>27,9 g</td> <td>116 kcal</td> </tr> <tr> <td>Kersen</td> <td>1 schaaltje (= 100 g)</td> <td>11,5 g</td> <td>54 kcal</td> </tr> <tr> <td>Kiwi</td> <td>1 stuks (= 75 g)</td> <td>7,7 g</td> <td>51 kcal</td> </tr> <tr> <td>Kumquat</td> <td>1 stuk (= 10 g)</td> <td>0,9 g</td> <td>5 kcal</td> </tr> <tr> <td>Lychee</td> <td>1 stuk (= 10 g)</td> <td>1,6 g</td> <td>7 kcal</td> </tr> <tr> <td>Mandarijn</td> <td>1 stuk (= 55 g)</td> <td>4,5 g</td> <td>25 kcal</td> </tr> <tr> <td>Mango</td> <td>1 schaaltje (= 100 g)</td> <td>13,9 g</td> <td>66 kcal</td> </tr> <tr> <td>Nectarine</td> <td>1 stuk (= 90 g)</td> <td>5,9 g</td> <td>32 kcal</td> </tr> <tr> <td>Papaja</td> <td>1 stuk (= 100 g)</td> <td>7,8 g</td> <td>39 kcal</td> </tr> <tr> <td>Passievrucht</td> <td>1 stuk (= 15 g)</td> <td>0,9 g</td> <td>8 kcal</td> </tr> <tr> <td>Peer (met schil)</td> <td>1 stuk (= 150 g)</td> <td>14,2 g</td> <td>82 kcal</td> </tr> <tr> <td>Perzik</td> <td>1 stuk (= 110 g)</td> <td>8,7 g</td> <td>45 kcal</td> </tr> <tr> <td>Pruim, gedroogd</td> <td>1 stuk (= 8 g)</td> <td>3,6 g</td> <td>20 kcal</td> </tr> <tr> <td>Pruim, vers</td> <td>1 stuk (= 40g)</td> <td>2,9 g</td> <td>18 kcal</td> </tr> <tr> <td>Rozijnen, gedroogd</td> <td>1 handje (= 35 g)</td> <td>25,5 g</td> <td>127 kcal</td> </tr> <tr> <td>Sinaasappel</td> <td>1 stuk (= 120 g)</td> <td>9,2 g</td> <td>61 kcal</td> </tr> <tr> <td>Vijgen, gedroogd</td> <td>1 stuk (= 20 g)</td> <td>9,6 g</td> <td>52 kcal</td> </tr> <tr> <td>Vijgen, vers</td> <td>1 stuk (= 50 g)</td> <td>9,5 g</td> <td>42 kcal</td> </tr> <tr> <td>Watermeloen</td> <td>1 schaaltje (= 100 g)</td> <td>8 g</td> <td>36 kcal</td> </tr> <tr> <td>Witte druiven</td> <td>1 schaaltje (= 100 g)</td> <td>15,6 g</td> <td>76 kcal</td> </tr> </tbody> </table>
En de CSS om de tabel een beetje uitzicht te geven (css/tablesort.css):
table { border-collapse: collapse; border: 0.05em solid black; border-radius: 5em / 5em; background-color: #b6ff00; } table caption { font-weight: bold; font-size: 125%; text-transform: uppercase; } .footnote { font-size: 75%; color: #666; } table caption, table th, table td, .footnote { font-family: Arial, Helvetica, sans-serif; padding: .5em; } table td { border: solid 1px #C35E4D; } table tbody th { color: #630; border-bottom: solid 1px #C35E4D; } table thead th { background: #ffe4cd ; border-bottom: solid 3px #E75D49; } /* css entities: https://www.w3schools.com/cssref/css_entities.asp */ .asc::after { content: "\0020\0020\0020\2191"; } .desc::after { content: "\0020\0020\0020\2193"; } table thead th:hover { cursor: hand; cursor: pointer; color: white; background-color: #630; }